 /*if(_lifeCycleFSM.getCurrentStateWork().suscessful()) {
 //Register this class as listener of the foran manager
 ((InitForanManagerWork)_lifeCycleFSM.getCurrentStateWork()).getForanManager().addListener(this);
 _lifeCycleFSM.fireFSMEvent("evt.inited");
 }
 else
 {
 _lifeCycleFSM.fireFSMEvent("evt.finish");
 }*/
package controlisolation.lifecyclefsm.controlcodegenerator;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;

public class LifeCycleFSMControlCodeGenerator implements SCXMLParserListener {

    private SCXMLParser _parser;
    
    private StringBuffer _generated_code;
    
    private String _lfsm_filename;
    private String _generatedClassname;
    private String _destinationPackage;
    private String _outputDirectory;
    
    
    private String _currentStateName;
    private List<String> _generatedDecissionControlMethodNames,
            _generatedErrorControlMethodNames;
    
    /* Method name: target,events */
    private Map<String,Map<String,List<String>>> _eventsPerTargetForMethodMap;
    
    public LifeCycleFSMControlCodeGenerator(
            String lfsm_filename,
            String generatedClassname,
            String destinationPackage,
            String outputDirectory)
    {
      _parser = new SCXMLParser();
      _parser.addListener(this);
      _lfsm_filename = lfsm_filename;
      _generatedClassname=generatedClassname;
      _destinationPackage=destinationPackage;
      _outputDirectory=outputDirectory;
      _generatedDecissionControlMethodNames=new ArrayList<String>();
      _generatedErrorControlMethodNames=new ArrayList<String>();
      _eventsPerTargetForMethodMap= new HashMap<String,Map<String,List<String>>>();  
    }
    
   

    public boolean init() {
        return _parser.init();
    }

    private String indentString(int level) {
        StringBuffer strb = new StringBuffer();
        for (int i = 0; i < level; i++) {
            strb.append("\t");
        }
        return strb.toString();
    }

    public String generateCode() {
        _generated_code = new StringBuffer();
        generateImportSection();
        generateControlClass();
        return _generated_code.toString();
    }

    private void generateImportSection()
    {
       _generated_code.append("package "+this._destinationPackage+";\n\n"); 
       //_generated_code.append("import controlisolation.lifecyclefsm.LifeCycleFSM;\n");
       _generated_code.append("import java.util.logging.Level;\n");
       _generated_code.append("import controlisolation.lifecyclefsm.LifeCycleFSMController;\n");
       _generated_code.append("import controlisolation.lifecyclefsm.NoWork;\n");
       _generated_code.append("import controlisolation.lifecyclefsm.stateexecutor.StateWork;\n");
       

       _generated_code.append("\n");
    }
    
    
    private void generateControlClass()
    {
        
        
        _generated_code.append("public class "+this._generatedClassname+" extends LifeCycleFSMController {\n");
        _generated_code.append(indentString(1));
        _generated_code.append("private NoWork _noWork=new NoWork();\n\n");
        _generated_code.append(indentString(1));
        _generated_code.append("@Override\n");
        _generated_code.append(indentString(1));
        _generated_code.append("public void controlCycle() throws Exception {\n");
        //_generated_code.append(indentString(2));
        //_generated_code.append("if (_running==false) return;\n");
        _generated_code.append(indentString(2));
        _generated_code.append("StateWork work;\n");
     
        _generated_code.append(indentString(2));
        _generated_code.append("work=_lfsm.getCurrentStateWork();\n");
        _generated_code.append(indentString(2));
        _generated_code.append("if (work==null) {\n"); 
        _generated_code.append(indentString(3));
        _generated_code.append("if (!_lfsm.isInitial(_lfsm.getCurrentState())){\n");
        _generated_code.append(indentString(4));
        _generated_code.append("_logger.log(Level.INFO,\"No work defined for state: using default!\");\n"); 
        _generated_code.append(indentString(3));
        _generated_code.append("}\n");
        _generated_code.append(indentString(2));
        _generated_code.append("work=_noWork;\n");
        _generated_code.append(indentString(2));
        _generated_code.append("}\n");
       
        //_generated_code.append("if (work==null) { _logger.log(Level.INFO,\"No work defined for state:"+_currentStateName+" using default!\"); work=_noWork; }\n");
        
        
        _parser.parse(_lfsm_filename);
        
        _generated_code.append(indentString(1));
        _generated_code.append("throw new Exception(\"Unexpected State\");\n");
        
        _generated_code.append(indentString(1));
        _generated_code.append("} //end of control method\n\n");
                
        _generated_code.append(generateClassMethods());
        
        _generated_code.append("} //end of class \n");
    }
    
    private String generateClassMethods()
    {
        StringBuffer strb=new StringBuffer();
        for (String str:_generatedDecissionControlMethodNames)
        {
            templateForDecissionControlMethod(strb, str);
            
        }
        
        for (String str:_generatedErrorControlMethodNames)
        {
            templateForErrorControlMethod(strb, str);
        }
        
        
        
        return strb.toString();
    }
    
    
    @Override
    public void stateStart(Map<String, String> attributesmap) {
        //System.out.println("Generate code for:"+attributesmap.get("id"));
        _generated_code.append(generateCodeForStateStart(attributesmap));
        _currentStateName=attributesmap.get("id");
    }

    @Override
    public void stateEnd(List<Map<String, String>> transitionAttributes) {

        _generated_code.append(generateCodeForStateEnd(transitionAttributes));
    }

    private String generateCodeForStateStart(Map<String, String> attributesmap) {
        String stateId = attributesmap.get("id");
        StringBuffer strb = new StringBuffer();
        strb.append(indentString(2));
        strb.append("if (_lfsm.getCurrentState().getId().equals(\""+stateId+"\")){\n");
        strb.append(indentString(2) + "/* Control Code for state " + stateId + " */\n");
        
        return strb.toString();
    }

    
    private String generateDecissionPointMethodName(String stateName)
    {
       String str;
       String stname=stateName.replace('.', '_');
       
       str="decissionControlFrom_"+stname;
       _generatedDecissionControlMethodNames.add(str);
       return str;
    }
    
    private String generateErrorControlPointMethodName(String stateName)
    {
       String str;
       String stname=stateName.replace('.', '_');
       str="errorControlFor_"+stname;
       _generatedErrorControlMethodNames.add(str);
       return str;
    }
    
    private String generateCodeForStateEnd(List<Map<String, String>> transitionAttributes) {
        StringBuffer strb = new StringBuffer();

        strb.append(indentString(2)
                + "/* Possible transitions to take at this point are: \n");
              
        for (Map<String, String> attr : transitionAttributes) {
            String eventName = attr.get("event");
            String targetName = attr.get("target");
            if (eventName == null) {
                strb.append(indentString(2) + "unnamed transition event found. Check your diagram.\n");
            } else {
                strb.append(indentString(2)
                        + eventName + " will take to " + targetName + "\n");
            }
        }
        strb.append(indentString(2));
        strb.append("*/\n");

        strb.append(indentString(2));
        strb.append("if(work.suscessful()){\n");
        boolean has_node_to_same_node_cycle=false;
        {       
            
            String method_name= generateDecissionPointMethodName(_currentStateName);
            // get the set of different target states
            ArrayList<String> targets=new ArrayList<String>();
            for (Map<String, String> attr : transitionAttributes) {
                String targetName = attr.get("target");
                if (targetName==null) //cycle to the same state
                {
                   targetName=_currentStateName; 
                }
                if (!targets.contains(targetName))
                {
                    targets.add(targetName);
                }
            }
           HashMap<String,List<String>> EventsbyTargetState=new HashMap<String,List<String>>();
           for (String target:targets)
           {
               if (EventsbyTargetState.get(target)==null)
               {
                   EventsbyTargetState.put(target, new ArrayList<String>());
               }
               for (Map<String, String> attr : transitionAttributes) {
                String eventName = attr.get("event");
                String targetName = attr.get("target");
                 if (targetName==null) //cycle to the same state
                {
                   targetName=_currentStateName;
                   has_node_to_same_node_cycle=true;
                }
                 
                if (target.equals(targetName))
                {
                    List<String> evtList=(List<String>)EventsbyTargetState.get(target);
                    evtList.add(eventName);
                }
               }
               _eventsPerTargetForMethodMap.put(method_name, EventsbyTargetState);
           }
           strb.append(indentString(2));
           strb.append("_lfsm.fireFSMEvent("+method_name+"(work));\n");
           
            
           
        }
        if (has_node_to_same_node_cycle)
        {
           strb.append(indentString(2));
           strb.append("// Cycles to same node are not supported (works are not relaunched\n");
           strb.append(indentString(2));
           strb.append("// Check diagram on state:"+_currentStateName+"\n");
           strb.append(indentString(2));
           strb.append("throw new RuntimeException(\"Cycles to same node are not supported\");\n");
        }
        strb.append(indentString(2));
        strb.append("} else /* work produced fatal errors or exceptions */ {\n");
        strb.append(indentString(3));
        strb.append(generateErrorControlPointMethodName(_currentStateName)+"(work);\n");
        strb.append(indentString(2));
        strb.append("}\n");
        strb.append(indentString(2) + "return; } \n");
        strb.append(indentString(2));
        strb.append("/* State Control Code Ended */\n\n");
        return strb.toString();
    }



    private void templateForDecissionControlMethod(StringBuffer strb, String method_name) {
        strb.append(indentString(1));
        strb.append("private String "+method_name+"(StateWork work) {\n");
        strb.append(indentString(2));
        strb.append("/* Possibe target states from this state: \n");
        Map<String,List<String>> targetEvents=this._eventsPerTargetForMethodMap.get(method_name);
        for (String target:targetEvents.keySet())
        {
            List<String> eventnames=targetEvents.get(target);
            strb.append(indentString(3));
            for (String evt:eventnames)
            {
                strb.append("| "+evt+" | ");
            }
            strb.append("\n");
            strb.append(indentString(3));
            strb.append(" will result in jumping to state: "+target+"\n");
        }
        
        strb.append(indentString(3));
        strb.append("Do event selection logic here */\n");
        strb.append(indentString(1));
        boolean just_one_event=false;
        if (targetEvents.size()==1)
        {
          String key=(String)targetEvents.keySet().toArray()[0];
          List<String> eventnames=targetEvents.get(key);
          if (eventnames.size()==1)
          {
            strb.append("return \""+eventnames.get(0) +"\";\n");
            just_one_event=true;
          }
        }    
        if (just_one_event==false)
        {
            strb.append("return \"choose_an_event\";\n");
        }
        strb.append(indentString(1));
        strb.append("}\n");
    }
    
    private void templateForErrorControlMethod(StringBuffer strb, String str) {
        strb.append(indentString(1));
        strb.append("private void "+str+"(StateWork work) {\n");
        strb.append(indentString(2));
        strb.append("unifiedErrorControl(work);\n");
        strb.append(indentString(1));
        strb.append("}\n");
    }

    
    private static void copyFile(File sourceFile, File destFile) throws IOException {
    if(!destFile.exists()) {
        destFile.createNewFile();
    }

    FileChannel source = null;
    FileChannel destination = null;

    try {
        source = new FileInputStream(sourceFile).getChannel();
        destination = new FileOutputStream(destFile).getChannel();
        destination.transferFrom(source, 0, source.size());
    }
    finally {
        if(source != null) {
            source.close();
        }
        if(destination != null) {
            destination.close();
        }
    }
}
    
    
    public void outputGeneratedCodeToFile() throws FileNotFoundException 
    {
       String classFilePath=this._outputDirectory+"/"+this._generatedClassname+".java";
       String bkfilepath=classFilePath+"_"+System.currentTimeMillis()+"bkp";
       
       File fcheck=new File(classFilePath);
       //If file exists, backup it.
       if (fcheck.exists())
       {
          File bkfile=new File(bkfilepath);
          try{
          copyFile(fcheck,bkfile);
          } catch(Exception e)
          {
              System.out.println(e.toString());
          }
       }
       
       PrintWriter out=new PrintWriter(classFilePath);
       //generateCode();
       //System.out.println(_code);
       out.print(_generated_code.toString());
       out.close();
    }
    
  
}
